//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Globalization;
using System.Reflection;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using EntLibContrib.Common.Configuration;
using EntLibContrib.Resource.Configuration;
using EntLibContrib.Resource.Managers;
using EntLibContrib.Resource.Properties;

namespace EntLibContrib.Resource.Providers
{
	/// <summary>
	/// Assembly Resource Provider.
	/// </summary>
	/// <remarks>
	/// This provider loads an assembly and generates a <see cref="ExtendedComponentResourceManager" /> populated
	/// with resources embedded in the assembly or a satellite based on the resource base name within the assembly.
	/// <para>Note: a <see cref="ExtendedComponentResourceManager" /> can only manage one resource at a time even
	/// if an assembly has multiple resources. Additional Resource Managers will be required for each Resource set
	/// within an assembly.</para>
	/// </remarks>
	[ConfigurationElementType(typeof(AssemblyResourceProviderData))]
	public class AssemblyResourceProvider : ResourceProvider
	{
		#region Fields
		private string resourceAssemblyName;
		#endregion

		#region Construction
		/// <summary>
		/// Initializes a new default instance of the <see cref="AssemblyResourceProvider"/> class.
		/// </summary>
		/// <param name="resourceAssemblyName">Name of the resource assembly.</param>
		/// <param name="resourceBaseName">Name of the resource base.</param>
		public AssemblyResourceProvider(string resourceAssemblyName, string resourceBaseName)
			: this(resourceAssemblyName, resourceBaseName, false)
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="AssemblyResourceProvider"/> class.
		/// </summary>
		/// <param name="resourceAssemblyName">Name of the resource assembly.</param>
		/// <param name="resourceBaseName">Name of the resource base.</param>
		/// <param name="isInternal">if set to <c>true</c> then the provider is for internal resources.</param>
		public AssemblyResourceProvider(string resourceAssemblyName, string resourceBaseName, bool isInternal)
			: base(resourceBaseName, isInternal)
		{
			// if the provider is for internal resources then validation must be bypassed,
			// otherwise we end up in circles.
			if (!isInternal)
				ValidateParameter(resourceAssemblyName);

			this.resourceAssemblyName = resourceAssemblyName;
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Create a Resource Manager to manage the resource for an assembly resource provider
		/// </summary>
		/// <returns>A <see cref="ExtendedComponentResourceManager" /> instance</returns>
		/// <remarks>
		/// <para>This method makes a type of <see cref="ExtendedComponentResourceManager"/> instance publicly available
		/// based on the assembly name and base name retrieved during Initialisation</para>
		/// </remarks>
		public override ExtendedComponentResourceManager CreateResourceManager()
		{  
			// Check for first time use of resource manager
			if (ResourceManager == null)
			{
				// Generate a Resource Manager
				Assembly resourceAssembly = Assembly.Load(resourceAssemblyName);
				ResourceManager = new ExtendedComponentResourceManager(ResourceBaseName, resourceAssembly, typeof(CommonResourceSet));
				ResourceManager.IgnoreCase = true;
			}
			return ResourceManager;
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Validates the assembly name parameter.
		/// </summary>
		/// <param name="assemblyName">Name of the assembly.</param>
		private void ValidateParameter (string assemblyName)
		{
			// check for a null assembly name
			if (string.IsNullOrEmpty(assemblyName))
				throw new ArgumentException(InternalResources.ExceptionAssemblyResourceAssemblyNameNullOrEmpty);

			// check that the assembly name represents a real assembly
			Assembly assembly;
			try
			{
				assembly = Assembly.Load(assemblyName);
			}
			catch (Exception e)
			{
				throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, InternalResources.ExceptionAssemblyResourceInvalidAssembly, assemblyName), e);
			}

			// check that the assembly actually contains resources
			if (assembly.GetManifestResourceNames().Length == 0)
				throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, InternalResources.ExceptionAssemblyResourceNoResources, assemblyName));

			// iterate over the available resources to find a match with the base name
			bool foundBaseName = false;
			foreach ( string baseName in assembly.GetManifestResourceNames() )
			{
				if ( baseName == ResourceBaseName + CommonSettings.AssemblyExtension )
				{
					foundBaseName = true;
					break;
				}
			}

			// Check to see if a match was found
			if ( !foundBaseName )
				throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, InternalResources.ExceptionAssemblyResourceNoBaseMatch, ResourceBaseName));
		}
		#endregion
	}
}
